﻿using System;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Object = UnityEngine.Object;

namespace ZyGame
{
    public class Config<T> : ScriptableObject where T : ScriptableObject
    {
        private Action _disposeable;
        private static T _instance;

        public static T instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = Load();
                }

                return _instance;
            }
        }

        public void Dispose()
        {
            _disposeable?.Invoke();
            Client.Resource.Release(_instance);
            _instance = null;
            GC.SuppressFinalize(this);
        }

        public static T Load()
        {
            PathOptions options = typeof(T).GetCustomAttribute<PathOptions>();
            if (options is null)
            {
                throw new NullReferenceException(nameof(PathOptions));
            }

            //Debug.Log($"Load {options.localtion} Config: {options.filepath}");
            string fileName = Path.GetFileName(options.filepath);
            T instance = null;

            switch (options.localtion)
            {
                case PathOptions.Localtion.Packaged | PathOptions.Localtion.Internal:
                    break;
                //这里要处理一下Internal|Packaged,出现这种情况，优先加载Packaged，如果Package加载失败在加载Internal
                case PathOptions.Localtion.Project:
                case PathOptions.Localtion.Internal:
#if UNITY_EDITOR
                    instance = UnityEditorInternal.InternalEditorUtility.LoadSerializedFileAndForget(options.filepath).FirstOrDefault() as T;
                    if (instance is null)
                    {
                        instance = CreateInstance<T>();
                        instance.name = nameof(T);
                    }
#else
                    instance = Resources.Load<T>(Path.GetFileNameWithoutExtension(options.filepath));
#endif
                    break;
                case PathOptions.Localtion.Packaged:
                    TextAsset textAsset = Client.Resource.LoadAsset<TextAsset>(options.filepath);
                    instance = CreateConfig(textAsset);
                    Client.Resource.Release(textAsset);
                    break;
                case PathOptions.Localtion.File:
                    if (options.filepath.EndsWith("asset"))
                    {
                        if (!Client.File.Exists(fileName))
                        {
                            break;
                        }

                        byte[] bytes = Client.File.ReadData(fileName);
                        string json = bytes.GetString();
                        instance = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
                    }
                    else if (options.filepath.EndsWith("json"))
                    {
                        if (!Client.File.Exists(fileName))
                        {
                            break;
                        }

                        byte[] bytes = Client.File.ReadData(fileName);
                        string json = bytes.GetString();
                        instance = CreateConfig(new TextAsset(json));
                    }

                    break;
            }

            return instance;
        }

        private static T CreateConfig(TextAsset textAsset)
        {
            if (textAsset is null)
            {
                return default;
            }

            try
            {
                return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(textAsset.text);
            }
            catch
            {
                var instance = CreateInstance<T>();
                if (instance is ISerializeble serialized)
                {
                    if (serialized.Deserialized(textAsset.text))
                    {
                        return instance;
                    }
                }

                return default;
            }
        }

        public static void Save()
        {
            PathOptions options = typeof(T).GetCustomAttribute<PathOptions>();
            if (options is null)
            {
                throw new NullReferenceException(nameof(PathOptions));
            }

            if (_instance is null)
            {
                return;
            }

            switch (options.localtion)
            {
                case PathOptions.Localtion.Project:
                case PathOptions.Localtion.Internal:
#if UNITY_EDITOR
                    UnityEditorInternal.InternalEditorUtility.SaveToSerializedFileAndForget(new Object[1] { _instance }, options.filepath, true);
#endif
                    break;
                case PathOptions.Localtion.Packaged:
                case PathOptions.Localtion.File:
                    if (options.filepath.EndsWith("asset"))
                    {
#if UNITY_EDITOR
                        UnityEditor.EditorUtility.SetDirty(_instance);
                        break;
#endif
                    }

                    if (instance is ISerializeble serialized)
                    {
                        File.WriteAllText(options.filepath, serialized.Serialized());
                        break;
                    }

                    File.WriteAllText(options.filepath, Newtonsoft.Json.JsonConvert.SerializeObject(_instance));
                    break;
            }
        }
    }
}